// 定义构造函数 创建 轮播图实例化对象
class CreateBannerObj {
    // 构造器 
    // 参数1    element 创建轮播图的标签对象
    // 参数2    array   创建轮播图的图片数据
    constructor(element, array) {
        // 使用 属性 存储形参数据
        this.ele = element;
        this.arr = array;
        // 定义 属性 存储 多个函数都需要使用的数据
        // 定义 属性 存储 全局变量
        this.ul;
        this.ol;
        this.div;
        this.ulLis;
        this.olLis;
        this.liWidth;
        this.index = 1;
        this.time;
        this.flag = true;
    }

    // 函数方法

    // 定义入口函数 指定调用所有的程序
    init(){
        this.setPage();
        this.autoLoop();
        this.setMouse();
        this.setClick();
        this.setHide();
    }

    // 动态生成页面
    setPage() {

        // 闯将 ul ol div 标签对象
        this.ul = document.createElement('ul');
        this.ol = document.createElement('ol');
        this.div = document.createElement('div');

        // 定义变量 存储 ul>li字符串 ol>li字符串 div中的字符串
        let ulLiStr = '';
        let olLiStr = '';
        // div标签中是 固定内容
        let divStr = '<button name="left">&lt;</button><button name="right">&gt;</button>';;

        // 设定 ul>li 和 ol>li 标签对象字符串
        this.arr.forEach((item, index) => {
            // item  是 数组中存储图片数据的对象
            // index 是 数组中存储图片的对象的索引下标
            ulLiStr += `<li><img src="${item.src}" alt=""></li>`;
            olLiStr += index === 0 ? `<li class="active" name="olLi" num="${index}"></li>` : `<li name="olLi" num="${index}"></li>`;
        })

        // 将 设定好的 字符串写入 标签对象中
        this.ul.innerHTML = ulLiStr;
        this.ol.innerHTML = olLiStr;
        this.div.innerHTML = divStr;

        // 将 ul ol div 标签 按照顺序 写入 轮播图标签中
        this.ele.appendChild(this.ul);
        this.ele.appendChild(this.ol);
        this.ele.appendChild(this.div);

        // 获取 所有的 ul>li ol>li
        this.ulLis = this.ul.querySelectorAll('li');
        this.olLis = this.ol.querySelectorAll('li');

        // 克隆 第一个ul>li 最后一个ul>li
        const cloneFirst = this.ulLis[0].cloneNode(true);
        const cloneLast = this.ulLis[this.arr.length - 1].cloneNode(true);

        // 将 克隆的第一个ul>li写入 ul标签 结束位置
        this.ul.appendChild(cloneFirst);
        // 将 克隆的最后一个ul>li写入 ul标签 起始位置
        this.ul.insertBefore(cloneLast, this.ulLis[0]);

        // 获取 一个 ul>li 宽度占位
        this.liWidth = this.ulLis[0].offsetWidth;

        // 设定 ul标签宽度  当前ul>li个数 * 一个 ul>li 宽度 拼接 px单位
        this.ul.style.width = (this.arr.length + 2) * this.liWidth + 'px';

        // 定位 ul标签 向左 定位 一个 ul>li 宽度
        this.ul.style.left = -this.liWidth + 'px';
    }


    // 自动轮播
    autoLoop() {
        // 自动轮播函数 this指向 是 实例化对象
        // console.log( this );

        // 设定定时器 使用 属性存储 定时器编号
        this.time = setInterval(() => {
            // this.index 累加 1 
            this.index++;

            // 调用函数 设定焦点样式
            // 在 构造函数的函数方法中 调用其他函数方法
            // 也是通过 实例化对象.函数方法() 来 调用
            // 语法 也就是 this.函数方法()
            this.setFocusStyle();

            // 调用 move() 运动函数 实现 轮播图 运动切换
            // 给 move() 运动函数 第三个参数赋值回调函数
            // 1 赋值的是 回调函数的函数名称 this.loopEnd
            // 2 回调函数的this指向 此时 是 undefined 不是 实例化对象
            //   赋值的是函数名称 使用 bind语法 改变this指向 
            //   当前 构造函数中 this指向是 实例化对象 
            //   绑定 回调函数 新的this指向是 当前 构造函数的this指向 
            //   也就是 实例化对象
            //   ! 回调函数 赋值 函数名称 改变this指向 使用 bind语法形式
            move(this.ul, { left: -this.index * this.liWidth }, this.loopEnd.bind(this));

        }, 3000)

    }

    // 焦点样式切换
    setFocusStyle() {
        // 清除 所有 ol>li 标签 class,active
        this.olLis.forEach(item => {
            // item 是 ol>li 标签对象 
            item.classList.remove('active');
        })

        // 给 当前显示的 ul>li 对应的 ol>li 添加 class,active
        if (this.index === 0) {
            // 如果 this.index 是 0 
            // 也就是 显示的当前ul>li的 第一个 
            // 给 ol>li 的 最后一个 添加 class.active
            this.olLis[this.olLis.length - 1].classList.add('active');


        } else if (this.index === this.arr.length + 1) {
            // 如果 this.index 是 this.arr.length+1 
            // 也就是 显示的当前ul>li的 最后一个 
            // 给 ol>li 的 第一个 添加 class.active
            this.olLis[0].classList.add('active');

        } else {
            // 其他情况 是 给 this.index -1 对应的 ol>li 添加 class,active
            this.olLis[this.index - 1].classList.add('active');

        }
    }

    // 自动轮播运动结束的回调函数
    loopEnd() {
        // 当 loopEnd 函数 以 回调函数形式执行时 
        // 整个函数的this指向 都会改变
        // this指向变成 undefined 
        // 需要通过 bind 语法形式 改变this指向 为 实例化对象
        // console.log( this )

        // 判断 显示的轮播图 切换 轮播图定位
        if (this.index === this.arr.length + 1) {
            // 如果 显示的是 当前 ul>li 最后一个 
            // this.index 赋值 1 
            // 也就是 运动结束切换到 ul>li 第二个 
            // 也就是 原始轮播图 第一个
            this.index = 1;

            // 瞬间定位 切换 ul
            this.ul.style.left = -this.index * this.liWidth + 'px';

        } else if (this.index === 0) {
            // 如果 显示的是 当前 ul>li 第一个
            // this.index 赋值 this.arr.length 
            // 也就是 运动结束切换到 ul>li 倒数第二个
            this.index = this.arr.length;

            // 瞬间定位 切换 ul
            this.ul.style.left = -this.index * this.liWidth + 'px';

        }

        // 给 开关变量 赋值 true 可以 再次点击
        this.flag = true;

    }


    // 鼠标的移入移出 
    setMouse() {
        // 给 轮播图div添加 鼠标移入事件 
        this.ele.addEventListener('mouseenter', () => {
            // 清除定时器 
            clearInterval(this.time);
        })

        // 给 轮播图div添加 鼠标移出事件 
        this.ele.addEventListener('mouseleave', () => {
            // 再次调用自动轮播函数
            this.autoLoop();
        })
    }

    // 点击事件
    setClick() {
        // 给 轮播图div 添加 点击事件 
        // 通过 事件委托 给标签添加点击效果
        this.ele.addEventListener('click', event => {

            if (event.target.getAttribute('name') === 'left') {
                // 防止点击过快
                if (!this.flag) return;
                this.flag = false;

                // this.index 执行 累减 1 操作
                // 表示 显示 上一张 轮播图
                this.index--;

                // 调用函数 设定焦点样式
                this.setFocusStyle();

                // 调用 move() 运动函数 完成 ul标签定位切换
                move(this.ul, { left: -this.index * this.liWidth }, this.loopEnd.bind(this));

            } else if (event.target.getAttribute('name') === 'right') {
                // 防止点击过快
                if (!this.flag) return;
                this.flag = false;

                // this.index 执行 累加 1 操作
                // 表示 显示 下一张 轮播图
                this.index++;

                // 调用函数 设定焦点样式
                this.setFocusStyle();

                // 调用 move() 运动函数 完成 ul标签定位切换
                move(this.ul, { left: -this.index * this.liWidth }, this.loopEnd.bind(this));

            } else if (event.target.getAttribute('name') === 'olLi') {
                // 防止点击过快
                if (!this.flag) return;
                this.flag = false;

                // this.index 赋值 触发事件的ol>li标签
                // num属性存储的属性值+1 
                this.index = Number(event.target.getAttribute('num')) + 1;

                // 调用函数 设定焦点样式
                this.setFocusStyle();

                // 调用 move() 运动函数 完成 ul标签定位切换
                move(this.ul, { left: -this.index * this.liWidth }, this.loopEnd.bind(this));

            }
        })

    }

    // 浏览器最小化
    setHide() {
        // 给 document 添加 浏览器显示状态改变监听事件
        document.addEventListener('visibilitychange', ()=>{
            // 浏览器显示状态描述 
            if (document.visibilityState === 'hidden') {
                // 清除 自动轮播定时器
                clearInterval(this.time);

            } else if (document.visibilityState === 'visible') {
                // 再次调用自动轮播函数
                this.autoLoop();

            }


        })


    }
}